/**
 * Copyright (c) 2005-20010 springside.org.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 
 * $Id: PropertyFilter.java 1205 2010-09-09 15:12:17Z calvinxiu $
 */
package cn.gbase.jiangsu.data.transfer.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.nutz.castor.Castors;
import org.nutz.dao.Cnd;
import org.nutz.dao.sql.Criteria;
import org.nutz.dao.util.cri.Exps;
import org.nutz.dao.util.cri.Like;
import org.nutz.dao.util.cri.SqlExpression;
import org.nutz.dao.util.cri.SqlExpressionGroup;

/**
 * 与具体ORM实现无关的属性过滤条件封装类, 主要记录页面中简单的搜索过滤条件.
 * 
 * @author calvin
 */
public class PropertyFilter {

	/** 多个属性间OR关系的分隔符. */
	public static final String OR_SEPARATOR = "_OR_";

	/** 属性比较类型. */
	public enum MatchType {
		EQ, LIKE, LT, GT, LE, //<=
		GE, //>=
		MOREEQ, //(a=1 or a=2) 例子: filters.add(new PropertyFilter("MOREEQS_checkstatus", Const.XHJK_XZ+"_"+Const.XHJK_DH ));
		RLIKE, NOEQ, // 不等于,
		ISNOT, // is not null
		ISNULL, // is null
		NOLIKE, // not like,
		NORLIKE, // not like
		BENULL;

	}

	/** 属性数据类型. */
	public enum PropertyType {
		S(String.class), I(Integer.class), L(Long.class), N(Double.class), D(Date.class), B(Boolean.class);

		private Class<?> clazz;

		private PropertyType(Class<?> clazz) {
			this.clazz = clazz;
		}

		public Class<?> getValue() {
			return clazz;
		}
	}

	private String filterName;

	private MatchType matchType = null;
	private Object matchValue = null;

	private Class<?> propertyClass = null;
	private String[] propertyNames = null;
	private String propertyNameStr;

	public PropertyFilter() {
	}

	/**
	 * @param filterName 比较属性字符串,含待比较的比较类型、属性值类型及属性列表. 
	 *                   eg. LIKES_NAME_OR_LOGIN_NAME
	 * @param value 待比较的值.
	 */
	public PropertyFilter(final String filterName, final String value) {

		this.filterName = filterName;
		String firstPart = StringUtils.substringBefore(filterName, "_");
		String matchTypeCode = StringUtils.substring(firstPart, 0, firstPart.length() - 1);
		String propertyTypeCode = StringUtils.substring(firstPart, firstPart.length() - 1, firstPart.length());

		try {
			matchType = Enum.valueOf(MatchType.class, matchTypeCode);
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性比较类型.", e);
		}

		try {
			propertyClass = Enum.valueOf(PropertyType.class, propertyTypeCode).getValue();
		} catch (RuntimeException e) {
			throw new IllegalArgumentException("filter名称" + filterName + "没有按规则编写,无法得到属性值类型.", e);
		}

		propertyNameStr = StringUtils.substringAfter(filterName, "_");
		//Assert.isTrue(StringUtils.isNotBlank(propertyNameStr), "filter名称" + filterName + "没有按规则编写,无法得到属性名称.");
		propertyNames = StringUtils.splitByWholeSeparator(propertyNameStr, PropertyFilter.OR_SEPARATOR);

		this.matchValue = Castors.me().castTo(value, propertyClass);
		if ((propertyClass == PropertyType.D.getValue()) && (matchType == MatchType.LE || matchType == MatchType.LT)) {
			Date date = (Date) matchValue;
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(date);
			calendar.set(Calendar.HOUR_OF_DAY, 23);
			calendar.set(Calendar.MINUTE, 59);
			calendar.set(Calendar.SECOND, 59);
			this.matchValue = calendar.getTime();
		}
	}

	/**
	 * 通过给定条件构造filter
	 * @param propertyNameStr 属性名
	 * @param matchType    比较类型
	 * @param matchValue    比较值
	 */
	public PropertyFilter(final String propertyNameStr, final MatchType matchType, PropertyType propertyType,
			final Object matchValue) {
		this.filterName = matchType.toString() + propertyType.toString() + "_" + propertyNameStr;
		this.matchType = matchType;
		//this.propertyClass = propertyType.getValue();
		this.propertyNameStr = propertyNameStr;
		propertyNames = StringUtils.splitByWholeSeparator(propertyNameStr, PropertyFilter.OR_SEPARATOR);
		this.matchValue = matchValue;
	}

	/**
	 * 从HttpRequest中创建PropertyFilter列表, 默认Filter属性名前缀为filter.
	 * 
	 * @see #buildFromHttpRequest(HttpServletRequest, String)
	 */
	public static List<PropertyFilter> buildFromHttpRequest(final HttpServletRequest request) {
		return buildFromHttpRequest(request, "filter");
	}

	/**
	 * 从HttpRequest中创建PropertyFilter列表
	 * PropertyFilter命名规则为Filter属性前缀_比较类型属性类型_属性名.
	 * 
	 * eg.
	 * filter_EQS_name
	 * filter_LIKES_name_OR_email
	 */
	public static List<PropertyFilter> buildFromHttpRequest(final HttpServletRequest request, final String filterPrefix) {
		List<PropertyFilter> filterList = new ArrayList<PropertyFilter>();

		//从request中获取含属性前缀名的参数,构造去除前缀名后的参数Map.
		Map<String, Object> filterParamMap = ServletUtils.getParametersStartingWith(request, filterPrefix + "_");

		//分析参数Map,构造PropertyFilter列表
		for (Map.Entry<String, Object> entry : filterParamMap.entrySet()) {
			String filterName = entry.getKey();
			String value = (String) entry.getValue();
			//如果value值为空,则忽略此filter.
			if (StringUtils.isNotBlank(value)) {
				PropertyFilter filter = new PropertyFilter(filterName, value);
				filterList.add(filter);
			}
		}

		return filterList;
	}

	private static SqlExpression toSqlExp(String propertyName, MatchType matchType, Object matchValue) {
		SqlExpression sqlExp = null;
		if (MatchType.EQ.equals(matchType)) {
			sqlExp = Exps.eq(propertyName, matchValue);
		} else if (MatchType.LIKE.equals(matchType)) {
			sqlExp = Exps.like(propertyName, matchValue.toString(), false);
		} else if (MatchType.RLIKE.equals(matchType)) {
			Like rLikeExp = Exps.like(propertyName, matchValue.toString(), false);
			rLikeExp.left("");
			sqlExp = rLikeExp;
		} else if (MatchType.LT.equals(matchType)) {
			sqlExp = Exps.create(propertyName, "<", matchValue);
		} else if (MatchType.GT.equals(matchType)) {
			sqlExp = Exps.create(propertyName, ">", matchValue);
		} else if (MatchType.LE.equals(matchType)) {
			sqlExp = Exps.create(propertyName, "<=", matchValue);
		} else if (MatchType.GE.equals(matchType)) {
			sqlExp = Exps.create(propertyName, ">=", matchValue);
		}
		return sqlExp;
	}

	/**
	 * 获取比较值的类型.
	 */
	public Class<?> getPropertyClass() {
		return propertyClass;
	}

	/**
	 * 获取比较方式.
	 */
	public MatchType getMatchType() {
		return matchType;
	}

	/**
	 * 获取比较值.
	 */
	public Object getMatchValue() {
		return matchValue;
	}

	/**
	 * 获取比较属性名称列表.
	 */
	public String[] getPropertyNames() {
		return propertyNames;
	}

	/**
	 * 获取唯一的比较属性名称.
	 */
	public String getPropertyName() {
		//Assert.isTrue(propertyNames.length == 1, "There are not only one property in this filter.");
		return propertyNames[0];
	}

	/**
	 * 是否比较多个属性.
	 */
	public boolean hasMultiProperties() {
		return (propertyNames.length > 1);
	}

	public String getPropertyNameStr() {
		return propertyNameStr;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((filterName == null) ? 0 : filterName.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		PropertyFilter other = (PropertyFilter) obj;
		if (filterName == null) {
			if (other.filterName != null) {
				return false;
			}
		} else if (!filterName.equals(other.filterName)) {
			return false;
		}
		return true;
	}

	/***
	 * 从属性过滤器中生成sql查询条件.
	 * @param filters
	 * @return
	 */
	public static Criteria cri(final HttpServletRequest request, String... orderArgs) {
		Criteria cri = Cnd.cri();
		List<PropertyFilter> filters = PropertyFilter.buildFromHttpRequest(request);
		for (PropertyFilter filter : filters) {
			if (filter.hasMultiProperties()) {
				SqlExpressionGroup orSqlExp = Cnd.exps(toSqlExp(filter.getPropertyNames()[0], filter.getMatchType(),
						filter.getMatchValue()));
				for (int i = 1; i < filter.getPropertyNames().length; i++) {
					orSqlExp.or(Cnd.exps(toSqlExp(filter.getPropertyNames()[i], filter.getMatchType(),
							filter.getMatchValue())));
				}
				cri.where().and(orSqlExp);
			} else {
				cri.where().and(toSqlExp(filter.getPropertyName(), filter.getMatchType(), filter.getMatchValue()));
			}
		}
		if (orderArgs != null && orderArgs.length > 0) {
			for (int i = 0; i < orderArgs.length; i = i + 2) {
				String orderDire = orderArgs[i + 1];
				if ("ASC".equalsIgnoreCase(orderDire)) {
					cri.getOrderBy().asc(orderArgs[i]);
				} else {
					cri.getOrderBy().desc(orderArgs[i]);
				}
			}
		}
		return cri;
	}

	/***
	 * 从属性过滤器中生成sql查询条件.
	 * @param filters
	 * @return
	 */
	public static Cnd toCnd(List<PropertyFilter> filters, String... orderArgs) {
		Cnd cnd = Cnd.where("1", "=", 1);
		for (PropertyFilter filter : filters) {
			if (filter.hasMultiProperties()) {
				SqlExpressionGroup orSqlExp = Cnd.exps(toSqlExp(filter.getPropertyNames()[0], filter.getMatchType(),
						filter.getMatchValue()));
				for (int i = 1; i < filter.getPropertyNames().length; i++) {
					orSqlExp.or(Cnd.exps(toSqlExp(filter.getPropertyNames()[i], filter.getMatchType(),
							filter.getMatchValue())));
				}
				cnd.and(orSqlExp);
			} else {
				cnd.and(toSqlExp(filter.getPropertyName(), filter.getMatchType(), filter.getMatchValue()));
			}
		}
		if (orderArgs != null && orderArgs.length > 0) {
			for (int i = 0; i < orderArgs.length; i = i + 2) {
				String orderDire = orderArgs[i + 1];
				if ("ASC".equalsIgnoreCase(orderDire)) {
					cnd.asc(orderArgs[i]);
				} else {
					cnd.desc(orderArgs[i]);
				}
			}
		}
		return cnd;
	}
}
